#include "qopenharmonyplayer.h"
#include <rawfile/raw_file_manager.h>

#include <QString>
#include <QHash>
#include <QReadWriteLock>
#include <QNetworkRequest>
#include <QUrl>
#include <QFile>
#include <QDebug>

typedef QHash<OH_AVPlayer *, QOpenHarmonyPlayer *> MediaPlayers;
Q_GLOBAL_STATIC(MediaPlayers, mediaPlayers)
Q_GLOBAL_STATIC(QReadWriteLock, rwLock)

QT_BEGIN_NAMESPACE

NativeResourceManager *QOpenHarmonyPlayer::m_resourceManager = nullptr;

QOpenHarmonyPlayer::QOpenHarmonyPlayer()
    : QObject()
{
    QWriteLocker locker(rwLock);
    m_player = OH_AVPlayer_Create();
    if (m_player != nullptr) {
        m_playerCallback.onInfo = &QOpenHarmonyPlayer::onPlayerInfo;
        m_playerCallback.onError = &QOpenHarmonyPlayer::onPlayerError;
        OH_AVPlayer_SetPlayerCallback(m_player, m_playerCallback);
        mediaPlayers->insert(m_player, this);
    }
}

QOpenHarmonyPlayer::~QOpenHarmonyPlayer()
{
    if (m_player != nullptr) {
        QWriteLocker locker(rwLock);
        mediaPlayers->remove(m_player);
        OH_AVPlayer_Release(m_player);
    }
}

void QOpenHarmonyPlayer::release()
{
    OH_AVPlayer_Release(m_player);
}

void QOpenHarmonyPlayer::reset()
{
    OH_AVPlayer_Reset(m_player);
}

int QOpenHarmonyPlayer::getCurrentPosition()
{
    if ((m_state == AV_PLAYING)
            || (m_state == AV_PAUSED)
            || (m_state == AV_COMPLETED)) {
        int time = 0;
        OH_AVPlayer_GetCurrentTime(m_player, &time);
        return time;
    }
    return 0;
}

int QOpenHarmonyPlayer::getDuration()
{
    if (m_state < AV_PREPARED)
        return 0;
    int duration = 0;
    OH_AVPlayer_GetDuration(m_player, &duration);
    return duration;
}

bool QOpenHarmonyPlayer::isPlaying()
{
    return m_state == AV_PLAYING;
}

int QOpenHarmonyPlayer::volume()
{
    return m_volume * 100;
}

bool QOpenHarmonyPlayer::isMuted()
{
    return qFuzzyCompare(m_volume, 0.0);
}

qreal QOpenHarmonyPlayer::playbackRate()
{
    qreal rate(1.0);

    AVPlaybackSpeed speed;
    OH_AVPlayer_GetPlaybackSpeed(m_player, &speed);
    switch (speed) {
    case AV_SPEED_FORWARD_0_75_X:
        rate = 0.75;
        break;
    case AV_SPEED_FORWARD_1_00_X:
        rate = 1.0;
        break;
    case AV_SPEED_FORWARD_1_25_X:
        rate = 1.25;
        break;
    case AV_SPEED_FORWARD_1_75_X:
        rate = 1.75;
        break;
    case AV_SPEED_FORWARD_2_00_X:
        rate = 2.0;
        break;
    }
    return rate;
}

void QOpenHarmonyPlayer::play()
{
    if (m_state == AV_PLAYING)
        return;
    if ((m_state == AV_PREPARED)
            || (m_state == AV_PAUSED)
            || (m_state == AV_COMPLETED)) {
        OH_AVPlayer_Play(m_player);
    } else if (m_state == AV_STOPPED) {
        m_isPendingPlaying = true;
        prepare();
    }
    else {
        m_isPendingPlaying = true;
    }
}

void QOpenHarmonyPlayer::pause()
{
    if (m_state == AV_PLAYING)
        OH_AVPlayer_Pause(m_player);
}

void QOpenHarmonyPlayer::stop()
{
    if ((m_state == AV_PLAYING) ||
            (m_state == AV_COMPLETED) ||
            (m_state == AV_PREPARED) ||
            (m_state == AV_PAUSED)) {
        OH_AVPlayer_Stop(m_player);
    }
}

void QOpenHarmonyPlayer::seekTo(qint32 msec)
{
    if ((m_state == AV_PLAYING) ||
            (m_state == AV_PAUSED) ||
            (m_state == AV_COMPLETED)) {
        OH_AVPlayer_Seek(m_player, msec, AV_SEEK_NEXT_SYNC);
    }
}

void QOpenHarmonyPlayer::setMuted(bool mute)
{
    if (mute) {
        m_volumeBeforeMute = m_volume;
        setVolume(0);
    } else {
        setVolume(m_volumeBeforeMute);
    }
}

void QOpenHarmonyPlayer::setDataSource(const QNetworkRequest &request)
{
    if (m_state != AV_IDLE)
        reset();

    QUrl url = request.url();
    if (url.isEmpty())
        return;
    QString scheme = url.scheme();
    if (scheme == "rawfile") {
        QString _fileName = url.path();
        if (_fileName.startsWith("/"))
            _fileName.remove(0, 1);
        QByteArray byteArray = _fileName.toLatin1();
        RawFile *file = OH_ResourceManager_OpenRawFile(m_resourceManager, byteArray.constData());
        if (file != nullptr) {
            RawFileDescriptor desc;
            if (OH_ResourceManager_GetRawFileDescriptor(file, desc)) {
                OH_AVPlayer_SetFDSource(m_player, desc.fd, desc.start, desc.length);
                OH_ResourceManager_ReleaseRawFileDescriptor(desc);
            }
            OH_ResourceManager_CloseRawFile(file);
        }
    } else if (scheme == "file") {
        QFile file(url.path());
        if (file.open(QFile::ReadOnly)) {
            int length = file.size();
            int fd = file.handle();
            OH_AVPlayer_SetFDSource(m_player, fd, 0, length);
            file.close();
        } else {
            qWarning() << "Could not open media:" << file.errorString();
            emit mediaStatusChanged(QMediaPlayer::InvalidMedia);
        }
    } else {
        QString urlString = url.toString();
        QByteArray dataArray = urlString.toLatin1();
        OH_AVPlayer_SetURLSource(m_player, dataArray.constData());
    }
}

void QOpenHarmonyPlayer::prepare()
{
    OH_AVPlayer_Prepare(m_player);
}

void QOpenHarmonyPlayer::setVolume(int volume)
{
    m_volume = volume;
    OH_AVPlayer_SetVolume(m_player, m_volume / 100.0, m_volume / 100.0);
}

bool QOpenHarmonyPlayer::setPlaybackRate(qreal rate)
{
    AVPlaybackSpeed speed;
    if (rate < 0.75)
        speed = AV_SPEED_FORWARD_0_75_X;
    else if (rate < 1.0)
        speed = AV_SPEED_FORWARD_1_00_X;
    else if (rate < 1.25)
        speed = AV_SPEED_FORWARD_1_25_X;
    else if (rate < 1.75)
        speed = AV_SPEED_FORWARD_1_75_X;
    else
        speed = AV_SPEED_FORWARD_2_00_X;
    return OH_AVPlayer_SetPlaybackSpeed(m_player, speed) == AV_ERR_OK;
}

void QOpenHarmonyPlayer::setDisplay(OHNativeWindow *window)
{
    if (window == nullptr)
        return;
    m_window = window;
    if (m_state == AV_INITIALIZED) {
        OH_AVPlayer_SetVideoSurface(m_player, m_window);
        m_surfaceSetted = true;
        OH_AVPlayer_Prepare(m_player);
    }
}

OHNativeWindow *QOpenHarmonyPlayer::display() const
{
    return m_window;
}

void QOpenHarmonyPlayer::setErrorString(int code, const QString &errorString)
{
    emit error(code, errorString);
}

void QOpenHarmonyPlayer::setPlayerInfo(AVPlayerOnInfoType type, int32_t extra)
{
    switch(type) {
    case AV_INFO_TYPE_STATE_CHANGE:
        setState(extra);
        break;
    case AV_INFO_TYPE_RESOLUTION_CHANGE:
        obtainVideoSize();
        emit videoAvailableChanged(true);
        break;
    case AV_INFO_TYPE_DURATION_UPDATE:
        emit durationChanged(extra);
        break;
    case AV_INFO_TYPE_POSITION_UPDATE:
        emit progressChanged(extra);
        break;
    case AV_INFO_TYPE_BUFFERING_UPDATE:
        emit bufferingChanged(extra);
        break;
    default:
        break;
    }
}

void QOpenHarmonyPlayer::setState(int32_t state)
{
    m_state = AVPlayerState (state);
    switch(state) {
    case AV_IDLE:
        release();
        break;
    case AV_INITIALIZED: // avplayer 设置播放源后触发该状态上报
        if (!m_surfaceSetted && m_window != nullptr) {
             OH_AVPlayer_SetVideoSurface(m_player, m_window);
             m_surfaceSetted = true;
             prepare();
        }
        break;
    case AV_PREPARED: // prepare调用成功后上报该状态机
        obtainVideoSize();
        mediaStatusChanged(QMediaPlayer::LoadedMedia);
        if (m_isPendingPlaying) {
            OH_AVPlayer_Play(m_player);
        }
        emit audioAvailableChanged(true);
        emit seekableChanged(true);
        break;
    case AV_STOPPED:
        emit seekableChanged(false);
        break;
    case AV_COMPLETED:
        emit mediaStatusChanged(QMediaPlayer::EndOfMedia);
        break;
    case AV_RELEASED:
        emit durationChanged(0);
        emit progressChanged(0);

        emit audioAvailableChanged(false);
        emit videoAvailableChanged(false);
        emit seekableChanged(true);
        break;
    default:
        break;
   }
   stateChanged(state);
}

static napi_value setResourceManager(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (argc != 1) {
        return nullptr;
    }
    napi_valuetype type;
    napi_typeof(env, args[0], &type);
    QOpenHarmonyPlayer::m_resourceManager = OH_ResourceManager_InitNativeResourceManager(env, args[0]);
    return nullptr;
}

void QOpenHarmonyPlayer::init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] ={
        {"setResourceManager", nullptr, setResourceManager, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
}

void QOpenHarmonyPlayer::obtainVideoSize()
{
    int32_t w, h = 0;
    OH_AVErrCode result = OH_AVPlayer_GetVideoWidth(m_player, &w);
    if (result != AV_ERR_OK)
        return;
    result = OH_AVPlayer_GetVideoHeight(m_player, &h);
    if (result != AV_ERR_OK)
        return;
    emit videoSizeChanged(w, h);
}

void QOpenHarmonyPlayer::onPlayerInfo(OH_AVPlayer *player, AVPlayerOnInfoType type, int32_t extra)
{
    QOpenHarmonyPlayer *p = mediaPlayers->value(player);
    if (p == nullptr)
        return;
    p->setPlayerInfo(type, extra);
}

void QOpenHarmonyPlayer::onPlayerError(OH_AVPlayer *player, int32_t errorCode, const char *errorMsg)
{
    QOpenHarmonyPlayer *p = mediaPlayers->value(player);
    if (p == nullptr)
        return;
    p->setErrorString(errorCode, QString::fromLatin1(errorMsg));
}

AVPlayerState QOpenHarmonyPlayer::state() const
{
    return m_state;
}

QT_END_NAMESPACE
